package com.cn.lotary.base.dao;

import com.cn.lotary.base.web.LazyEntityDataModel;
import com.cn.lotary.util.*;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import javax.faces.application.FacesMessage;
import javax.faces.model.SelectItem;
import javax.persistence.*;
import javax.persistence.metamodel.SingularAttribute;
import javax.validation.ValidationException;
import org.primefaces.model.SortOrder;

/**
 *
 * @author Jason Chen Dec 8, 2010 version 1.0
 */
//TODO: detailed exception log
public abstract class BaseDao<T extends AbstractEntity> implements DaoInterfaces<T>, Serializable {

    public BaseDao() {
    }

    /**
     *
     * @return EntityManager, Entity class, primaryKey
     */
    //protected abstract BaseDaoParam<T> getBaseDaoParam();
    public abstract Class<T> getEntityClass();

    protected abstract EntityManager getEntityManager();

    /**
     * check unique column
     *
     * entity eg:
     *
     * <code>@Table(name = "account", uniqueConstraints = {
     * @UniqueConstraint(columnNames = {"name"})})</code>
     *
     * @param t entity
     * @return String column data
     */
    private String validationUniqueColumn(T t) {
        if (t == null) {
            return null;
        }
        String message = null;
        Table tableAnnotation;
        try {
            tableAnnotation = t.getClass().getAnnotation(Table.class);
        } catch (NullPointerException e) {
            LoggerUtils.warn(e);
            return message;
        }

        UniqueConstraint[] uniqueConstraints = tableAnnotation.uniqueConstraints();
        if (uniqueConstraints.length != 0) {
            for (int i = 0; i < uniqueConstraints.length; i++) {
                String[] columnNames = uniqueConstraints[i].columnNames();
                for (int j = 0; j < columnNames.length; j++) {
                    String column = columnNames[j];
                    Class<? extends AbstractEntity> cls = t.getClass();
                    try {
                        Method method = cls.getMethod(ObjectUtil.columnNameToGetMethos(column));
                        Object invoke;
                        invoke = method.invoke(t);
                        String field = ObjectUtil.columnNameToFieldName(column);
                        List<T> entitys = findByField(field, invoke);
                        if (t.isNew()) {
                            if (!entitys.isEmpty()) {
                                return invoke.toString();
                            }
                        } else {
                            if (entitys.size() == 1 && entitys.get(0).getId() == t.getId()) {
                                //TODO: 检查性能
                                //return null;
                                break;
                            } else if (entitys.size() > 0) {
                                return invoke.toString();
                            }
                        }

                    } catch (Exception ex) {
                        //TODO:logger
                        LoggerUtils.error(ex);
                    }
                }
            }
        }
        return message;
    }

    @Override
    public FacesMessage create(T t) {
        FacesMessage outMessage = createCondition(t);
        if (outMessage != null) {
            return outMessage;
        }
        //Annotation[] annotations = t.getClass().getAnnotations();outMessage = new FacesMessage(
        outMessage = new FacesMessage(FacesMessage.SEVERITY_INFO,
                MessageBundle.CREATE,
                MessageBundle.SUCCESS);
        String message = validationUniqueColumn(t);
        if (message != null) {
            outMessage.setSeverity(FacesMessage.SEVERITY_WARN);
            outMessage.setSummary(MessageBundle.DUPLICATION_OF_DATA);
            outMessage.setDetail(message);
            return outMessage;
        }
        try {
            if (t.isNew()) {              //TODO: set id

               // t.setId(UUID.randomUUID().toString().replaceAll("-", "").toUpperCase());
            }

            getEntityManager().persist(t);
            LoggerUtils.info(getEntityClass(), "[create]:" + t);
            return outMessage;
        } catch (ValidationException e) {
            //TODO:i18n
            GlobalException.setException("BaseDao_001", e);
        } catch (EntityExistsException e) {
            LoggerUtils.error(e);
        } catch (IllegalArgumentException e) {
            LoggerUtils.error(e);
        } catch (TransactionRequiredException e) {
            LoggerUtils.error(e);
        } catch (Exception e) {
            LoggerUtils.error(e);
            GlobalException.setException("BaseDao_001", e);
        }
        outMessage.setDetail(MessageBundle.EXCEPTION);
        outMessage.setSeverity(FacesMessage.SEVERITY_WARN);
        return outMessage;
    }

    /**
     * native query<br/>
         *
     * @param sql native sql
     * @return the number of entities updated or deleted
     */
    public int excuteNativeSql(String sql) {

        try {
            Query q = getEntityManager().createNativeQuery(sql);
            return q.executeUpdate();
        } catch (IllegalStateException e) {
            LoggerUtils.error(e);
        } catch (QueryTimeoutException e) {
            LoggerUtils.error(e);
        } catch (TransactionRequiredException e) {
            LoggerUtils.error(e);
        } catch (PersistenceException e) {
            LoggerUtils.error(e);
        } catch (Exception e) {
            LoggerUtils.error(e);
        }

        return 0;
    }

    /**
     *
     * @param filter eg: id > 1 and o.name = 'jason'
     * @return LazyEntityDataModel primefaces datatable
     */
    protected LazyEntityDataModel<T> getLazyEntityDataModel(String filter) {
        return new LazyEntityDataModel<T>(filter, this);
    }

    /**
     * id type only: Integer,String,Long
     *
     *
     * @param id
     * @return entity
     */
    @Override
    public T find(String id) {
        //TODO: lock result
        try {
            if(id != null && !"".equals(id)){
                T t = getEntityManager().find(getEntityClass(), id);
                return t;
            }
        } catch (Exception e) {
            LoggerUtils.error(e);
            GlobalException.setException("BaseDao_001", e);
        }
        return null;
    }

    /**
     *
     * @param first first row
     * @param pageSize row count
     * @param order order by
     * @return SELECT o FROM T o ORDER BY o.order
     */
    public List<T> findAll(int first, int pageSize, String order) {
        getEntityManager().getEntityManagerFactory().getCache().evictAll();
        String sql = "SELECT o FROM " + getEntityClass().getSimpleName() + " o";

        if (order != null) {
            sql = sql + " ORDER BY o." + order;
        }

        try {
            TypedQuery<T> q = createQuery(sql, getEntityClass());
            q.setFirstResult(first);
            q.setMaxResults(pageSize);

            return q.getResultList();
        } catch (IllegalArgumentException e) {
            LoggerUtils.error(e);
            GlobalException.setException("BaseDao_001", e);
        }

        return new ArrayList<T>();
    }

    @Override
    public List<T> find(int first, int pageSize, Map<String, String> filters, String sort) {
        String sql = "SELECT o FROM " + getEntityClass().getSimpleName() + " o";
        sql = formatSql(filters, sql, null);

        if (sort != null) {
            sql = sql + " ORDER BY o." + sort;
        }

        try {
            TypedQuery<T> q = createQuery(sql, getEntityClass());
            q.setFirstResult(first);
            q.setMaxResults(pageSize);

            return q.getResultList();
        } catch (IllegalArgumentException e) {
            LoggerUtils.error(e);
            GlobalException.setException("BaseDao_001", e);
        }
        return new ArrayList<T>();
    }

    private String formatSql(Map<String, String> filters, String sql) {
        StringBuilder outcome = new StringBuilder(sql);

        if (filters != null && !filters.isEmpty()) {
            outcome.append(" WHERE");

            for (String key : filters.keySet()) {
                String value = filters.get(key);
                outcome.append(" AND o.").append(key).append("='");
                outcome.append(value).append("'");
            }
        }

        return outcome.toString().replace("WHERE AND", "WHERE");
    }

    /**
     *
     * @param filters key=entity field, value = data
     * @return
     */
    public T findSingle(Map<String, String> filters) {
        String sql = "SELECT o FROM " + getEntityClass().getSimpleName() + " o";
        sql = formatSql(filters, sql);

        try {
            TypedQuery<T> q = createQuery(sql, getEntityClass());

            return q.getSingleResult();
        } catch (IllegalArgumentException e) {
            LoggerUtils.error(e);
        } catch (Exception e) {
            LoggerUtils.error(e);
            GlobalException.setException("BaseDao_001", e);
        }
        return null;
    }

    /**
     * for primeface dataTable lazy model bug 如果filter包含order by，则sort失效，
     *
     * @param first
     * @param pageSize
     * @param filters
     * @param sort
     * @param filter
     * @return Entity List
     * @exception sort can not work on Boolean Type
     */
    //BUG 001 : sort can not work on Boolean Type,
    public List<T> findByCondition(int first, int pageSize, Map<String, String> filters, String sort, SortOrder sortOrder,
            String filter) {
        sort = sort == null ? null : sort + (sortOrder.equals(SortOrder.ASCENDING) ? " ASC" : " DESC");

        String sql = "SELECT o FROM " + getEntityClass().getSimpleName() + " o";

        sql = formatSql(filters, sql, filter);

        if (sort != null) {
            sql = sql + " ORDER BY o." + sort;
        }

        try {
            clearCache();
            TypedQuery<T> q = createQuery(sql, getEntityClass());
            q.setFirstResult(first);
            q.setMaxResults(pageSize);

            return q.getResultList();
        } catch (IllegalArgumentException e) {
            LoggerUtils.error(e);
            GlobalException.setException("BaseDao_001", e);
        }

        return new ArrayList<T>();
    }

    /**
     *
     * @param filters eg: map(name=jason,job=staff)
     * @param filter eg: id < 20 and name = 'jason' @return entity
     * count
     */
    public Integer findByConditionRowCount(Map<String, String> filters, String filter) {
        //getEntityManager().getEntityManagerFactory().getCache().evictAll();
        //TODO: remove order by code
        String sql = "SELECT o FROM " + getEntityClass().getSimpleName() + " o";
        sql = formatSql(filters, sql, filter).replace("SELECT o", "SELECT COUNT(o.id)");

        try {
            return Integer.parseInt(createQuery(sql).getSingleResult().toString());
        } catch (IllegalArgumentException e) {
            //TODO:logger
            LoggerUtils.error(e);
        } catch (PessimisticLockException e) {
            LoggerUtils.error(e);
        } catch (TransactionRequiredException e) {
            LoggerUtils.error(e);
        } catch (LockTimeoutException e) {
            LoggerUtils.error(e);
        } catch (PersistenceException e) {
            LoggerUtils.error(e);
        } catch (Exception e) {
            LoggerUtils.error(e);
        }
        return 0;
    }

    /**
     *
     * @param filters 查 询 条 件
     * @return total count
     */
    protected Number findCount(Map<String, String> filters) {
        String sql = "SELECT COUNT(o.id) FROM " + getEntityClass().getSimpleName() + " o";

        if (!filters.isEmpty()) {
            sql = formatSql(filters, sql, null);
        }

        try {
            Query q = createQuery(sql);
            return (Number) q.getSingleResult();
        } catch (IllegalArgumentException e) {
            LoggerUtils.error(e);
            GlobalException.setException("BaseDao_001", e);
        }

        return 0;
    }

    /**
     * 多条件查询
     *
     * eg: name=jaspn,job=staff
     *
     * @param filters key=entity field, value= field data
     * @return entity list
     */
    protected List<T> findByMultiColumn(Map<String, String> filters) {
        String sql = "SELECT o FROM " + getEntityClass().getSimpleName() + " o";

        if (!filters.isEmpty()) {
            sql = formatSql(filters, sql, null);
        }

        try {
            TypedQuery<T> q = createQuery(sql, getEntityClass());
            return q.getResultList();
        } catch (IllegalArgumentException e) {
            LoggerUtils.error(e);
            GlobalException.setException("BaseDao_001", e);
        }

        return new ArrayList<T>();
    }

    /**
     *
     * @param filter is Entity Column attribute
     * @return rows count
     */
    protected Integer findCount(String filter) {
        String sql = "SELECT COUNT(o.id) FROM " + getEntityClass().getSimpleName() + " o Where o." + filter;
        Number count = null;
        try {
            Query q = createQuery(sql);
            count = (Number) q.getSingleResult();
        } catch (IllegalArgumentException e) {
            LoggerUtils.error(e);
            GlobalException.setException("BaseDao_001", e);
        }

        return count == null ? 0 : count.intValue();
    }

    @Override
    public List<T> findAll() {
        //CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
        //cq.select(cq.from(getEntityClass()));
        try {
            String sql = "SELECT o FROM " + getEntityClass().getSimpleName() + " o";
            TypedQuery<T> q = createQuery(sql, getEntityClass());

            return q.getResultList();
        } catch (Exception e) {
            LoggerUtils.error(e);
        }
        return new ArrayList<T>();
    }

    /**
     * find entity list by sql
     *
     * @param sql native query
     * @return Entity List
     */
    protected List<T> findByNativeSql(String sql) {
        Query q = getEntityManager().createNativeQuery(sql, getEntityClass());
        List<T> list = new ArrayList<T>();
        try {
            list = q.getResultList();
        } catch (Exception e) {
            LoggerUtils.error(e);
        }

        return list;
    }

    /**
     * get entity row count
     *
     * @return row count
     */
    @Override
    public int getCount() {
        try {
//            CriteriaQuery<T> cq = (CriteriaQuery<T>) getEntityManager().getCriteriaBuilder().createQuery();
//            cq.from(getEntityClass());
//
//            cq.select(cq.getSelection());
//            Query q = createQuery(cq);
//
//            return ((Long) q.getSingleResult()).intValue();



            String sql = "select count(id) from " + getTableName();
            long start = System.currentTimeMillis();
            Query q = getEntityManager().createNativeQuery(sql);
            int p = ((Long) q.getSingleResult()).byteValue();
            long end = System.currentTimeMillis();
            System.err.println("getCount using time: " + (end - start));
            return p;
        } catch (QueryTimeoutException e) {
            LoggerUtils.error(e);
        } catch (Exception e) {
            LoggerUtils.error(e);
        }
        return 0;
    }

    /**
     * @deprecated @param tablename
     * @return all entity count
     */
    public int getCount(String tablename) {
        String sql = "select count(id) from " + tablename;
        Query q = getEntityManager().createNativeQuery(sql);
        return ((Long) q.getSingleResult()).byteValue();
    }

    /**
     * 添加删除条件
     *
     * @param t entity
     * @return FacesMessage
     */
    protected FacesMessage removeCondition(T t) {
        return null;
    }

    /**
     * 添加更新条件
     *
     * @param t entity
     * @return FacesMessage
     */
    protected FacesMessage updateCondition(T t) {
        return null;
    }

    /**
     * 添加新增条件
     *
     * @param t
     * @return FacesMessage
     */
    protected FacesMessage createCondition(T t) {
        return null;
    }

    /**
     * delete entity 可自动级联删除 如果级联删除为false，则删除失败
     *
     * 如果需要添加删除条件，请重写removeCondition方法
     *
     * 修复级联删除bug201210181400
     *
     * @param t entity
     * @return FacesMessage
     */
    @Override
    public FacesMessage remove(T t) {
        FacesMessage facesMessage = removeCondition(t);
        if (facesMessage != null) {
            return facesMessage;
        }
        facesMessage = new FacesMessage(MessageBundle.REMOVE);
        try {
            //TODO:SELECT LAST_INSERT_ID();
            Class<T> entityClass = getEntityClass();

            Field[] fields = entityClass.getDeclaredFields();

            for (Field f : fields) {
                //f.getType().isAssignableFrom(BaseEntity.class);
                Class<?> type = f.getType();
                if (type.isAssignableFrom(List.class)) {
                    //-- start process cascading deletes
                    OneToMany oneToMany = f.getAnnotation(OneToMany.class);
                    ManyToMany manyToMany = f.getAnnotation(ManyToMany.class);

                    if (oneToMany != null && oneToMany.orphanRemoval()) {
                        continue;
                    }
                    if (manyToMany != null) {
                        continue;
                    }
                    //-- end process cascading deletes
                    String getMethod = ObjectUtil.getGetMethodNameByFiledName(f.getName());
                    Method method = entityClass.getMethod(getMethod);
                    Object obj = method.invoke(t);
                    if ((obj != null && !((List) obj).isEmpty())) {
                        facesMessage.setDetail(MessageBundle.IN_USING_CAN_NOT_REMOVE);
                        facesMessage.setSeverity(FacesMessage.SEVERITY_WARN);
                        return facesMessage;
                    }
                }
            }

//            for (Method method : methods) {
//                String methodName = method.getName();
//                if (methodName.contains("_")) {
//                    continue;
//                }
//                Class<?> returnType = method.getReturnType();
//                if (returnType.equals(List.class)) {
//                    Object obj = method.invoke(t);
//                    //-- start process cascading deletes
//                    String fildeName = ObjectUtil.getEntityFildeNameByEntityMethodName(methodName);
//
//
//
//                    Field field = entityClass.getDeclaredField(fildeName);
//                    OneToMany oneToMany = field.getAnnotation(OneToMany.class);
//                    ManyToMany manyToMany = field.getAnnotation(ManyToMany.class);
//
//                    if (oneToMany != null && oneToMany.orphanRemoval()) {
//                        continue;
//                    }
//                    if (manyToMany != null) {
//                        continue;
//                    }
//                    //-- end process cascading deletes
//
//                    if ((obj != null && !((List) obj).isEmpty())) {
//                        facesMessage.setDetail(MessageBundle.IN_USING_CAN_NOT_REMOVE);
//                        facesMessage.setSeverity(FacesMessage.SEVERITY_WARN);
//                        return facesMessage;
//                    }
//                }
//            }

            getEntityManager().remove(getEntityManager().merge(t));
            LoggerUtils.info(getEntityClass(), "[remove]:" + t);

            facesMessage.setSeverity(FacesMessage.SEVERITY_INFO);
            facesMessage.setDetail(MessageBundle.SUCCESS);
            return facesMessage;
        } catch (Exception e) {
            //TODO:other exception
            GlobalException.setException("BaseDao_001", e);
            LoggerUtils.error(e);
            facesMessage.setSummary(MessageBundle.EXCEPTION);
        }
        facesMessage.setSeverity(FacesMessage.SEVERITY_WARN);
        return facesMessage;
    }

    @Override
    public FacesMessage update(T t) {
        FacesMessage outMessage = updateCondition(t);
        if (outMessage != null) {
            return outMessage;
        }
        outMessage = new FacesMessage(MessageBundle.UPDATE);
        String message = validationUniqueColumn(t);
        if (message != null) {
            outMessage.setSeverity(FacesMessage.SEVERITY_WARN);
            outMessage.setSummary(MessageBundle.DUPLICATION_OF_DATA);
            outMessage.setDetail(message);
            return outMessage;
        }
        //TODO: check why can not catch if instance is not an entity or is a removed entity on getEntityManager().merge(t)
        try {
            if (t.isNew()) {
                outMessage.setSeverity(FacesMessage.SEVERITY_WARN);
                outMessage.setDetail(MessageBundle.OBJECT_DOES_NOT_EXIST);
            } else {
                getEntityManager().merge(t);
                LoggerUtils.info(getEntityClass(), "[update]" + t);
                outMessage.setSeverity(FacesMessage.SEVERITY_INFO);
                outMessage.setDetail(MessageBundle.SUCCESS);
                return outMessage;
            }
        } catch (ValidationException e) {
            outMessage.setSeverity(FacesMessage.SEVERITY_WARN);
            LoggerUtils.error(e);
        } catch (IllegalArgumentException e) {
            LoggerUtils.error(e);
            outMessage.setSeverity(FacesMessage.SEVERITY_WARN);
        } catch (TransactionRequiredException e) {
            outMessage.setSeverity(FacesMessage.SEVERITY_WARN);
            LoggerUtils.error(e);
        } catch (Exception e) {
            outMessage.setSeverity(FacesMessage.SEVERITY_WARN);
            outMessage.setDetail(MessageBundle.EXCEPTION);
            GlobalException.setException("BaseDao_001", e);
            LoggerUtils.error(e);
        }
        return outMessage;
    }

    /**
     * for primefaces datatable
     *
     * @param filters
     * @param sql
     * @param filter like
     * @return sql
     */
    private String formatSql(Map<String, String> filters, String sql, String filter) {
        if (!filters.isEmpty() || (filter != null && !filter.equals(""))) {
            sql = sql + " WHERE ";
        }

        if (filter != null && !filter.equals("")) {
            sql = sql + "o." + filter + " AND ";
        }

        if (filters != null) {
            Set<String> columns = filters.keySet();
            for (String column : columns) {
                String condantion = filters.get(column);
                Object object = ObjectUtil.getPropertyType(getEntityClass(), column);
                if (object != null) {
                    if (ObjectUtil.isString(object)) {
                        sql = sql + "o." + column + " LIKE '%" + condantion + "%' AND ";
                    } else if (ObjectUtil.isInterger(object)) {
                        if (ObjectUtil.canConverterToInteger(condantion)) {
                            sql = sql + "o." + column + " = '" + condantion + "' AND ";
                        }
                    } else if (ObjectUtil.isDate(object)) {
                        if (DateUitl.isYyyyDDddDate(condantion)) {
                            sql = sql + "o." + column + " = '" + condantion + "' AND ";
                        }
                    }
                }
            }
        }

        if (sql.endsWith("AND ")) {
            sql = sql.substring(0, sql.length() - 4);
        }

        if (sql.endsWith("WHERE ")) {
            sql = sql.substring(0, sql.length() - 6);
        }
        return sql;
    }

    /**
     * find by entity field
     *
     * createQuery
     *
     * @param column Entity attribute
     * @param data condition
     * @return Entity List
     */
    public List<T> findByField(String column, Object data) {
        if (column == null || "".equals(column) || data == null) {
            return new ArrayList<T>();
        }
        String sql = "SELECT o FROM " + getEntityClass().getSimpleName() + " o WHERE o." + column + " = '" + data + "'";
        try {
            TypedQuery<T> q = createQuery(sql, getEntityClass());
            //q.setParameter("data", data);
            return q.getResultList();
        } catch (Exception e) {
            LoggerUtils.error(e, sql);
            return new ArrayList<T>();
        }
    }

    /**
     * createNativeQuery
     *
     *
     * @param column table column
     * @param data
     * @return entity list
     */
    protected List<T> findByTableColumn(String column, String data) {
        String sql = "SELECT * FROM " + getTableName() + " WHERE " + column + " = :data";
        Query q = getEntityManager().createNativeQuery(sql, getEntityClass());
        q.setParameter("data", data);
        try {
            return q.getResultList();
        } catch (Exception e) {
            LoggerUtils.error(e, sql);
        }
        return new ArrayList<T>();
    }

    /**
     *
     * @param column entity attribute
     * @param data attribute data
     * @return single entity
     */
    public T findSingleByColumn(String column, Object data) {
        String sql = "SELECT o FROM " + getEntityClass().getSimpleName() + " o WHERE o." + column + " = :data";
        try {
            TypedQuery<T> q = createQuery(sql, getEntityClass());
            q.setParameter("data", data);
            return q.getSingleResult();
        } catch (NoResultException e) {
            //LoggerUtils.error(e, sql);            
        } catch (NonUniqueResultException e) {
            //TODO: check newInstance
            //    return getEntityClass().newInstance();
            LoggerUtils.error(e, sql);
        } catch (Exception e) {
            LoggerUtils.error(e, sql);
        }
        return null;
    }

    /**
     * find by entity class and column field
     *
     * @param c entity class
     * @param field entity attribute, not table column
     * @param data column data
     * @return entity but not T
     */
    public Object findByColumnAndEntityClass(Class<?> c, String field, Object data) {
        String sql = "SELECT o FROM " + c.getSimpleName() + " o WHERE o." + field + " = :data";
        try {
            Query q = createQuery(sql);
            q.setParameter("data", data);
            return q.getSingleResult();
        } catch (NoResultException e) {
        } catch (NonUniqueResultException e) {
            LoggerUtils.error(e, "\r\n\t findByColumn: SQL:" + sql);
        } catch (Exception e) {
            LoggerUtils.error(e, "\r\n\t findByColumn: SQL:" + sql);
        }
        return null;
    }

    /**
     * find entity by JPQL
     *
     * @param jpql SELECT o Object o
     * @return Entity list
     */
    protected List<T> findByJPQL(String jpql) {
        try {
            return createQuery(jpql, getEntityClass()).getResultList();
        } catch (Exception e) {
            LoggerUtils.error(e, "\r\n\t findByJPQL: SQL:" + jpql);
            return new ArrayList<T>();
        }
    }

    /**
     * find Single Entity by JPQL
     *
     * @param jpql eg: SELECT o Object o
     * @return Single Entity
     */
    protected T findSingleByJPQL(String jpql) {
        try {
            return (T) createQuery(jpql, getEntityClass()).getSingleResult();
            //return createQuery(jpql, getEntityClass()).getSingleResult();
        } catch (Exception e) {
            LoggerUtils.error(e, "\r\n\t findSingleByJPQL: SQL:" + jpql);
            return null;
        }
    }

    /**
     *
     * @param selectOne multiple or radio
     * @param selectLable which column will show on the view page
     * @param column entity attribute
     * @param o condition
     * @return SelectItem[String]
     */
    public SelectItem[] getSelectItems(SingularAttribute selectLable, String column, Object o, boolean selectOne, boolean i18n) {
        //TODO: condtition
        List<T> entities;
        if (column != null && o != null) {
            entities = findByField(column, o);
        } else {
            entities = findAll();
        }

        int size = selectOne ? entities.size() + 1 : entities.size();
        //int size = entities.size() + 1;
        SelectItem[] items = new SelectItem[size];
        int i = 0;
        if (selectOne) {
            items[0] = new SelectItem("", MessageBundle.getLocalizedString("select"));
            i++;
        }
        try {
            for (T x : entities) {
                Class<?> cla = x.getClass();
                Object oo = x;
                String lab = ObjectUtil.columnNameToGetMethos(selectLable.getName());
//                if (lab.contains(".")) {
//                    String[] str = lab.split("\\.");
//                    for (int j = 0; j < str.length - 1; j++) {
//                        Method m = cla.getMethod(str[j].replace("(", "").replace(")", ""));
//                        if (!oo.getClass().equals(String.class)) {
//                            oo = m.invoke(oo);
//                            lab = str[j + 1];
//                            cla = oo.getClass();
//                        } else {
//                            break;
//                        }
//                    }
//                }

                Method lable = cla.getMethod(lab);
                String showLable = (String) lable.invoke(oo == null ? x : oo);
                if (i18n) {
                    showLable = MessageBundle.getLocalizedString(showLable);
                }
                items[i++] = new SelectItem(x, showLable);
            }
        } catch (Exception e) {
            //TODO: logger
            LoggerUtils.error(e);
            GlobalException.setException("BaseDao_001", e);
        }
        return items;
    }

    public SelectItem[] getSelectItems(SingularAttribute selectLable, String column, Object o, boolean selectOne) {
        //TODO: condtition
        List<T> entities;
        if (column != null && o != null) {
            entities = findByField(column, o);
        } else {
            entities = findAll();
        }

        int size = selectOne ? entities.size() + 1 : entities.size();
        //int size = entities.size() + 1;
        SelectItem[] items = new SelectItem[size];
        int i = 0;
        if (selectOne) {
            items[0] = new SelectItem("", MessageBundle.getLocalizedString("select"));
            i++;
        }
        try {
            for (T x : entities) {
                Class<?> cla = x.getClass();
                Object oo = x;
                String lab = ObjectUtil.columnNameToGetMethos(selectLable.getName());
                if (lab.contains(".")) {
                    String[] str = lab.split("\\.");
                    for (int j = 0; j < str.length - 1; j++) {
                        Method m = cla.getMethod(str[j].replace("(", "").replace(")", ""));
                        if (!oo.getClass().equals(String.class)) {
                            oo = m.invoke(oo);
                            lab = str[j + 1];
                            cla = oo.getClass();
                        } else {
                            break;
                        }

                    }
                }

                Method lable = cla.getMethod(lab);
                String showLable = (String) lable.invoke(oo == null ? x : oo);
                items[i++] = new SelectItem(x, showLable);
            }
        } catch (Exception e) {
            //TODO: logger
            LoggerUtils.error(e);
            GlobalException.setException("BaseDao_001", e);
        }
        return items;
    }

    /**
     * get entity by entity class and id
     *
     * @param c entity class
     * @param id entity id
     * @return Entity , but not T
     */
    protected Object find(Class<?> c, Object id) {
        return getEntityManager().find(c, id);
    }

    /**
     ** 根 据 列 查 找 最 新 插 入 实 体
     *
     * @param maxTokenColumn entity attribute, eg: name or createTime
     * @return entity
     */
    protected T findLastByToken(String maxTokenColumn) {
        //TODO:create time 如果有重复值报错
        //TODO: get last insert entity
        try {
            String maxTokenSql = "SELECT max(o.id) FROM " + getEntityClass().getSimpleName() + " o";

            Query maxTokenQuery = createQuery(maxTokenSql);

            String maxEntitySql = "SELECT o FROM " + getEntityClass().getSimpleName() + " o where o.id = :token";

            TypedQuery<T> maxEntityQuery = createQuery(maxEntitySql, getEntityClass());
            maxEntityQuery.setParameter("token", maxTokenQuery.getSingleResult());

            return maxEntityQuery.getSingleResult();
        } catch (Exception e) {
            LoggerUtils.error(e);
            return null;
        }
    }

    /**
     * 根 据 实 体 和 列 查 找 最 新 插 入 实 体
     *
     * @param c entity class
     * @param maxTokenColumn entity attribute, eg: name or createTime
     * @return entity
     */
    protected Object findLastByToken(Class<?> c, String maxTokenColumn) {

        try {
            String maxTokenSql = "SELECT max(o." + maxTokenColumn + ") FROM " + c.getSimpleName() + " o";

            Query maxTokenQuery = createQuery(maxTokenSql);

            String maxEntitySql = "SELECT o FROM " + c.getSimpleName() + " o where o." + maxTokenColumn + " = :token";

            Query maxEntityQuery = createQuery(maxEntitySql);
            maxEntityQuery.setParameter("token", maxTokenQuery.getSingleResult());

            return maxEntityQuery.getSingleResult();
        } catch (Exception e) {
            LoggerUtils.error(e);
            return null;
        }
    }

    /**
     *
     * @return entity table name
     */
    private String getTableName() {
        return getEntityClass().getAnnotation(Table.class).name();
    }

    private void clearCache() {
        try {
            getEntityManager().getEntityManagerFactory().getCache().evictAll();
        } catch (IllegalStateException e) {
            LoggerUtils.error(e);
        } catch (Exception e) {
            LoggerUtils.error(e);
        }
    }

    private Query createQuery(String sql) {
        try {
            EntityManager em = getEntityManager();
            if (!em.isOpen()) {
                EntityManagerFactory emf = em.getEntityManagerFactory();
                emf.createEntityManager();
            }
            return em.createQuery(sql);
        } catch (IllegalArgumentException e) {
            LoggerUtils.error(e);
        } catch (Exception e) {
            LoggerUtils.error(e);
        }
        return null;
    }

    private TypedQuery<T> createQuery(String sql, Class c) {
        try {
            EntityManager em = getEntityManager();
            if (!em.isOpen()) {
                EntityManagerFactory emf = em.getEntityManagerFactory();
                emf.createEntityManager();
            }
            return getEntityManager().createQuery(sql, c);
        } catch (IllegalArgumentException e) {
            LoggerUtils.error(e);
        } catch (Exception e) {
            LoggerUtils.error(e);
        }
        return null;
    }
//    @AroundInvoke
//    private Object interceptor(InvocationContext ic) {
//        Object o = null;
//        try {
//            Map<String, Object> contextData = ic.getContextData();
//            Set<String> keySet =
//                    contextData.keySet();
//            StringBuilder context = new StringBuilder();
//            for (String key : keySet) {
//                context.append(key)
//                        .append(" :")
//                        .append(contextData.get(key))
//                        .append("\r\n");
//            }
//            Object[] parameters = ic.getParameters();
//            StringBuilder parameter = new StringBuilder();
//            if (parameters != null) {
//                for (int i = 0; i < parameters.length; i++) {
//                    parameter.append(parameters[i] != null ? parameters[i].toString()
//                            : parameters[i]).append(" ");
//                }
//            }
//            //LoggerUtils.info(aClass,
//            //      "[method]" + methodName + " [parameters] " + parameters + " [context]" + context);
//
//            o = ic.proceed();
//        } catch (Exception e) { //
//            LoggerUtils.error(e);
//            GlobalException.setException("001", e);
//        }
//
//        return o;
//    }
}
